/**
* author: SamWu
* email：samwulqy@gmail.com
**/
<?php
require_once 'database.php';
require_once 'functions.php';

/**
 * 高级输入过滤
 */
function sanitizeInput($data, $type = 'string') {
    if (is_array($data)) {
        return array_map('sanitizeInput', $data);
    }
    
    $data = trim($data);
    $data = stripslashes($data);
    
    switch ($type) {
        case 'email':
            $data = filter_var($data, FILTER_SANITIZE_EMAIL);
            break;
        case 'int':
            $data = filter_var($data, FILTER_SANITIZE_NUMBER_INT);
            break;
        case 'float':
            $data = filter_var($data, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
            break;
        case 'url':
            $data = filter_var($data, FILTER_SANITIZE_URL);
            break;
        default:
            $data = htmlspecialchars($data, ENT_QUOTES | ENT_HTML5, 'UTF-8', false);
    }
    
    return $data;
}

/**
 * 加密数据
 */
function encryptData($data) {
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted = openssl_encrypt(
        json_encode($data),
        'aes-256-cbc',
        ENCRYPTION_KEY,
        0,
        $iv
    );
    return base64_encode($iv . $encrypted);
}

/**
 * 解密数据
 */
function decryptData($data) {
    $data = base64_decode($data);
    $iv = substr($data, 0, openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted = substr($data, openssl_cipher_iv_length('aes-256-cbc'));
    return json_decode(openssl_decrypt(
        $encrypted,
        'aes-256-cbc',
        ENCRYPTION_KEY,
        0,
        $iv
    ), true);
}

/**
 * 获取客户端真实IP（防止梯子）
 */
function getClientIP() {
    $ip = '';
    
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        $ip = trim($ipList[0]);
    } else {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    }
    
    return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '';
}

/**
 * 检查IP是否在黑名单
 */
function isIpBlacklisted($ip) {
    global $pdo;
    
    $stmt = $pdo->prepare("SELECT 1 FROM ip_blacklist 
                          WHERE ip_address = ? AND (expires_at IS NULL OR expires_at > NOW())");
    $stmt->execute([$ip]);
    return (bool)$stmt->fetch();
}

/**
 * 添加IP到黑名单
 */
function addToBlacklist($ip, $reason = '', $expiryHours = 24) {
    global $pdo;
    
    $expiresAt = $expiryHours ? date('Y-m-d H:i:s', time() + $expiryHours * 3600) : null;
    
    $stmt = $pdo->prepare("INSERT INTO ip_blacklist (ip_address, reason, expires_at)
                          VALUES (?, ?, ?)
                          ON DUPLICATE KEY UPDATE 
                          reason = VALUES(reason),
                          expires_at = VALUES(expires_at)");
    $stmt->execute([$ip, $reason, $expiresAt]);
}

/**
 * 速率限制检查
 */
function checkRateLimit($ip, $vuid = null) {
    global $pdo;
    
    // 每分钟限制
    $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM verification_logs 
                          WHERE ip_address = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 MINUTE)");
    $stmt->execute([$ip]);
    $minuteCount = $stmt->fetch()['count'];
    
    if ($minuteCount >= RATE_LIMIT_PER_MINUTE) {
        addToBlacklist($ip, 'Rate limit exceeded (minute)', 1);
        return false;
    }
    
    // 每小时限制
    $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM verification_logs 
                          WHERE ip_address = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)");
    $stmt->execute([$ip]);
    $hourCount = $stmt->fetch()['count'];
    
    if ($hourCount >= RATE_LIMIT_PER_HOUR) {
        addToBlacklist($ip, 'Rate limit exceeded (hour)', 24);
        return false;
    }
    
    // VUID特定限制
    if ($vuid) {
        $stmt = $pdo->prepare("SELECT monthly_max_requests FROM request_stats 
                              JOIN websites ON request_stats.website_id = websites.id
                              WHERE websites.vuid = ? AND current_month = ?");
        $stmt->execute([$vuid, date('Ym')]);
        $limits = $stmt->fetch();
        
        if ($limits && $limits['monthly_requests'] >= $limits['monthly_max_requests']) {
            return false;
        }
    }
    
    return true;
}

/**
 * 验证VUID有效性
 */
function verifyVUID($vuid) {
    global $pdo;
    
    $stmt = $pdo->prepare("SELECT * FROM websites WHERE vuid = ? AND is_active = TRUE");
    $stmt->execute([$vuid]);
    $website = $stmt->fetch();
    
    if (!$website) {
        throw new Exception('Invalid VUID or website not active');
    }
    
    return $website;
}

/**
 * 验证API凭证
 */
function verifyApiCredentials($apiKey, $secretKey) {
    global $pdo;
    
    $stmt = $pdo->prepare("SELECT w.* FROM api_keys ak 
                          JOIN websites w ON ak.website_id = w.id
                          WHERE ak.api_key = ? AND ak.secret_key = ? AND ak.is_active = TRUE");
    $stmt->execute([$apiKey, $secretKey]);
    $website = $stmt->fetch();
    
    if (!$website) {
        throw new Exception('Invalid API credentials');
    }
    
    // 更新最后使用时间
    $stmt = $pdo->prepare("UPDATE api_keys SET last_used_at = NOW() WHERE api_key = ?");
    $stmt->execute([$apiKey]);
    
    return $website;
}

/**
 * 生成验证令牌
 */
function generateVerificationToken($websiteId) {
    global $pdo;
    
    $token = generateSecureRandom(64);
    $expiresAt = date('Y-m-d H:i:s', time() + TOKEN_EXPIRY);
    $ip = getClientIP();
    
    $stmt = $pdo->prepare("INSERT INTO verification_tokens 
                          (website_id, token, expires_at, ip_address)
                          VALUES (?, ?, ?, ?)");
    $stmt->execute([$websiteId, $token, $expiresAt, $ip]);
    
    return $token;
}

/**
 * 验证令牌有效性
 */
function verifyToken($token) {
    global $pdo;
    
    $stmt = $pdo->prepare("SELECT * FROM verification_tokens 
                          WHERE token = ? AND expires_at > NOW() AND used = FALSE");
    $stmt->execute([$token]);
    $tokenData = $stmt->fetch();
    
    if (!$tokenData) {
        return false;
    }
    
    // 标记为已使用
    $stmt = $pdo->prepare("UPDATE verification_tokens 
                          SET used = TRUE, used_at = NOW() 
                          WHERE id = ?");
    $stmt->execute([$tokenData['id']]);
    
    return $tokenData;
}

/**
 * 更新统计并检查限制
 */
function updateStats($websiteId, $isSuccess) {
    global $pdo;
    
    $currentMonth = date('Ym');
    $failed = $isSuccess ? 0 : 1;
    $success = $isSuccess ? 1 : 0;
    
    // 原子性更新
    $stmt = $pdo->prepare("UPDATE request_stats 
                          SET total_requests = total_requests + 1,
                              monthly_requests = monthly_requests + 1,
                              failed_attempts = failed_attempts + ?,
                              successful_attempts = successful_attempts + ?
                          WHERE website_id = ? AND current_month = ?");
    $stmt->execute([$failed, $success, $websiteId, $currentMonth]);
    
    // 检查是否超过限制
    $stmt = $pdo->prepare("SELECT monthly_requests, monthly_max_requests 
                          FROM request_stats 
                          WHERE website_id = ? AND current_month = ?");
    $stmt->execute([$websiteId, $currentMonth]);
    $stats = $stmt->fetch();
    
    if ($stats && $stats['monthly_requests'] >= $stats['monthly_max_requests']) {
        throw new Exception('Monthly request limit exceeded', 'MONTHLY_LIMIT_EXCEEDED');
    }
}

/**
 * 检查并自动封禁可疑IP
 */
function checkSuspiciousActivity($ip) {
    global $pdo;
    
    // 检查过去1小时失败次数
    $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM verification_logs 
                          WHERE ip_address = ? AND result = 'failed' 
                          AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)");
    $stmt->execute([$ip]);
    $result = $stmt->fetch();
    
    // 如果1小时内失败超过5次，自动封禁24小时
    if ($result['count'] > 5) {
        addToBlacklist($ip, 'Automatic ban: Excessive failed attempts', 24);
        return true;
    }
    
    return false;
}

/**
 * 验证回调签名
 */
function verifyCallbackSignature($rawData, $signature) {
    $secret = getCallbackSecret();
    $expected = hash_hmac('sha256', $rawData, $secret);
    return hash_equals($expected, $signature);
}

/**
 * 获取回调密钥
 */
function getCallbackSecret() {
    global $pdo;
    
    static $secret;
    if ($secret) return $secret;
    
    $stmt = $pdo->query("SELECT value FROM system_config WHERE name = 'callback_secret'");
    $result = $stmt->fetch();
    
    if (!$result) {
        $newSecret = generateSecureRandom(64);
        $stmt = $pdo->prepare("INSERT INTO system_config (name, value) VALUES ('callback_secret', ?)");
        $stmt->execute([$newSecret]);
        return $newSecret;
    }
    
    return $result['value'];
}

/**
 * 执行高级验证
 */
function performAdvancedVerification($behaviorData) {
    // 分析鼠标移动
    $mouseScore = isset($behaviorData['mouse_movements']) ? 
        analyzeMouseMovements($behaviorData['mouse_movements']) : 0;
    
    // 分析点击模式
    $clickScore = isset($behaviorData['click_patterns']) ? 
        analyzeClickPatterns($behaviorData['click_patterns']) : 0;
    
    // 分析其他行为指标
    $behaviorScore = analyzeBehaviorIndicators($behaviorData);
    
    // 计算总分 (加权)
    $totalScore = ($mouseScore * 0.4) + ($clickScore * 0.4) + ($behaviorScore * 0.2);
    
    // 记录分数用于调试和分析
    $behaviorData['score'] = $totalScore;
    logVerificationAttempt($behaviorData);
    
    return $totalScore >= 60; // 通过阈值
}

/**
 * 分析鼠标移动行为
 */
function analyzeMouseMovements($movements) {
    if (count($movements) < 3) return 0;
    
    $score = 50;
    $totalDistance = 0;
    $timeTaken = 0;
    $directionChanges = 0;
    $lastDirection = null;
    
    for ($i = 1; $i < count($movements); $i++) {
        $prev = $movements[$i-1];
        $curr = $movements[$i];
        
        $dx = $curr['x'] - $prev['x'];
        $dy = $curr['y'] - $prev['y'];
        $distance = sqrt($dx*$dx + $dy*$dy);
        $time = $curr['timestamp'] - $prev['timestamp'];
        
        $totalDistance += $distance;
        $timeTaken += $time;
        
        if ($dx != 0 || $dy != 0) {
            $direction = atan2($dy, $dx);
            if ($lastDirection !== null) {
                $angleDiff = abs($direction - $lastDirection);
                if ($angleDiff > M_PI) {
                    $angleDiff = 2 * M_PI - $angleDiff;
                }
                if ($angleDiff > M_PI/4) {
                    $directionChanges++;
                }
            }
            $lastDirection = $direction;
        }
    }
    
    $avgSpeed = $timeTaken > 0 ? $totalDistance / $timeTaken : 0;
    
    if ($avgSpeed > 0.5 && $avgSpeed < 5) $score += 20;
    
    $changeRatio = $directionChanges / count($movements);
    if ($changeRatio > 0.1 && $changeRatio < 0.4) $score += 15;
    
    if ($directionChanges < 2 && $totalDistance > 100) $score -= 30;
    
    $patternVariance = calculatePatternVariance($movements);
    if ($patternVariance < 0.1) $score -= 20;
    
    return max(0, min(100, $score));
}

/**
 * 分析点击行为
 */
function analyzeClickPatterns($clicks) {
    if (count($clicks) < 2) return 50;
    
    $score = 50;
    $timings = [];
    $positions = [];
    
    for ($i = 1; $i < count($clicks); $i++) {
        $prev = $clicks[$i-1];
        $curr = $clicks[$i];
        
        $timeDiff = $curr['timestamp'] - $prev['timestamp'];
        $distance = sqrt(
            pow($curr['x'] - $prev['x'], 2) + 
            pow($curr['y'] - $prev['y'], 2)
        );
        
        $timings[] = $timeDiff;
        $positions[] = ['x' => $curr['x'], 'y' => $curr['y']];
    }
    
    $timingVariance = calculateVariance($timings);
    $avgTiming = array_sum($timings) / count($timings);
    
    if ($timingVariance > 10000) $score += 15;
    elseif ($timingVariance < 100) $score -= 20;
    
    if ($avgTiming > 100 && $avgTiming < 1000) $score += 10;
    
    $positionVariance = calculatePositionVariance($positions);
    if ($positionVariance > 10000) $score += 10;
    else $score -= 15;
    
    return max(0, min(100, $score));
}

/**
 * 记录验证尝试
 */
function logVerificationAttempt($data) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("INSERT INTO verification_logs 
                              (website_id, ip_address, user_agent, score, data, result, created_at)
                              VALUES (?, ?, ?, ?, ?, ?, NOW())");
        $stmt->execute([
            $data['website_id'] ?? null,
            $data['ip_address'] ?? getClientIP(),
            $data['user_agent'] ?? '',
            $data['score'] ?? 0,
            json_encode($data),
            $data['result'] ?? 'failed'
        ]);
    } catch (Exception $e) {
        error_log("Failed to log verification attempt: " . $e->getMessage());
    }
}
?>
